<html lang="en">

<head>
  <title>ent schema network</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tonsky/FiraCode@5.2/distr/fira_code.css">
  <script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/randomcolor/0.6.1/randomColor.min.js" integrity="sha512-vPeZ7JCboHcfpqSx5ZD+/jpEhS4JpXxfz9orSvAPPj0EKUVShU2tgy7XkU+oujBJKnWmu4hU7r9MMQNWPfXsYw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <style type="text/css">
    html * {
      font-family: 'Fira Code', monospace !important;
      font-size: 14px;
    }

    #schema {
      width: 100%;
      height: 100%;
      border: 1px solid lightgray;
    }

    .var-type {
      color: #4EC9B0;
    }

    table {
      padding: 2px 3px;
    }

    .vis-tooltip,
    .table-container {
      background-color: #1e1e1e !important;
      color: white;
    }

    tr {
      color: white;
    }
  </style>
</head>

<body>
  <div id="schema"></div>
  <br />
  <script type="text/javascript">
    
    const fieldsToTable = fields => {
      const container = document.createElement("div");
      container.setAttribute("class", "table-container")
      if (!fields) {
        container.innerText = "no fields";
        return container;
      }
      const tbl = document.createElement("table");
      const tblBody = document.createElement("tbody");
      for (const field of fields) {
        const row = document.createElement("tr");
        for (const key of ["name", "type"]) {
          const cell = document.createElement("td");
          const cellText = document.createTextNode(field[key]);
          if (key === "type") {
            cell.setAttribute("class", "var-type")
          }
          cell.appendChild(cellText);
          row.appendChild(cell);
        }
        tblBody.appendChild(row);
      }
      tbl.appendChild(tblBody);
      container.appendChild(tbl);
      return container;
    }

    
    const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"Forecast\",\"fields\":[{\"name\":\"temperature\",\"type\":\"float64\"},{\"name\":\"windSpeed\",\"type\":\"float64\"},{\"name\":\"precipitationAmount\",\"type\":\"float64\"},{\"name\":\"weatherCondition\",\"type\":\"forecast.WeatherCondition\"},{\"name\":\"dateTime\",\"type\":\"time.Time\"}]},{\"id\":\"Region\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"parentRegion\",\"type\":\"string\"},{\"name\":\"latitude1\",\"type\":\"float64\"},{\"name\":\"longitude1\",\"type\":\"float64\"},{\"name\":\"latitude2\",\"type\":\"float64\"},{\"name\":\"longitude2\",\"type\":\"float64\"},{\"name\":\"osmData\",\"type\":\"schema.OsmData\"}]},{\"id\":\"RegionType\",\"fields\":[{\"name\":\"type\",\"type\":\"string\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"password\",\"type\":\"[]byte\"},{\"name\":\"firstName\",\"type\":\"string\"},{\"name\":\"lastName\",\"type\":\"string\"}]},{\"id\":\"Weather\",\"fields\":[{\"name\":\"temperature\",\"type\":\"float64\"},{\"name\":\"humidity\",\"type\":\"float64\"},{\"name\":\"windSpeed\",\"type\":\"float64\"},{\"name\":\"weatherCondition\",\"type\":\"weather.WeatherCondition\"},{\"name\":\"precipitationAmount\",\"type\":\"float64\"},{\"name\":\"measurementDateTime\",\"type\":\"time.Time\"}]}],\"edges\":[{\"from\":\"Forecast\",\"to\":\"Region\",\"label\":\"region\"},{\"from\":\"Region\",\"to\":\"RegionType\",\"label\":\"regionType\"},{\"from\":\"Region\",\"to\":\"User\",\"label\":\"accountId\"},{\"from\":\"Weather\",\"to\":\"Region\",\"label\":\"region\"},{\"from\":\"Weather\",\"to\":\"Forecast\",\"label\":\"forecast\"}]}");
    const nodes = new vis.DataSet((entGraph.nodes || []).map(n =>
    ({
      id: n.id,
      label: n.id,
      color: randomColor({
        luminosity: 'light',
        hue: 'random',
      }),
      title: fieldsToTable(n.fields),
    })
    ));
    edgesCounter = {};
    
    
    const edgeKey = e => `${e.to}::${e.from}`
    const edges = new vis.DataSet((entGraph.edges || []).map(e => {
      const counter = (edgesCounter[edgeKey(e)] || 0) + 1;
      edgesCounter[edgeKey(e)] = counter;
      if (e.from === e.to) {
        return {
          ...e,
          physics: false,
          arrows: "to",
          type: 'curvedCW',
          selfReference: {
            size: (counter + 1) * 10,
            angle: (counter * 0.8) * Math.PI / 4,
            renderBehindTheNode: false
          }
        }
      }
      return { ...e, type: 'curvedCW', physics: false, arrows: "to", smooth: { type: 'curvedCW', roundness: Math.pow(-1, counter) * 0.2 * counter } }
    }));
    const options = {
      manipulation: false,
      edges: {
        physics: false,
        smooth: { type: 'curvedCW', roundness: 0.2 },
        arrows: "to",
      },
      nodes: {
        widthConstraint: 60,
        heightConstraint: 60,
        shape: "box",
        font: { align: "center" },
      },
      layout: {
        improvedLayout: true,
        hierarchical: {
          enabled: true,
          levelSeparation: 250,
        },
      },
      physics: {
        enabled: true,
        barnesHut: {
          springConstant: 0,
          avoidOverlap: 1,
          springConstant: 0
        },
        solver: "barnesHut",
        repulsion: {
          nodeDistance: 150,
          springConstant: 0,
          damping: 0,
          springLength: 0
        }
      }
    };
    const container = document.getElementById("schema");
    const gph = new vis.Network(container, { nodes, edges }, options);
  </script>
</body>

</html>
